/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#ifndef __VVIEW_H__
#define __VVIEW_H__

#include "../../Utils/vUtils.h"
#include <Math/vMath.h>
#include "../../Macros/vMemoryMacros.h"
#include "./vFrustum.h"

namespace Vendetta {
	/*!
	 *	Abstract view class.
	 *	Provides full interface to control the way we see things.
	 */
	class  vView {
	public:

		/*!
		 *	Constructor.
		 *	Sets current matrix mode to Projection.
		 */
		vView();

		/*!
		 *	Default destructor.
		 */
		virtual ~vView();

		/*!
		 *	Defines perspective viewing volume (frustum) given quad in 3D space:
		 *	(left, bottom, -near) + (right, top, -near) and far value.
		 *	Far and near values are distances from view point to, respectively
		 *	far and near clipping planes.
		 *
		 *	\param leftBottomNear left, bottom & near values
		 *	\param rightTopFar right, top & far values
		 */
		virtual vVoid SetFrustum(const vVector3D &leftBottomNear, const vVector3D &rightTopFar) = 0;

		/*!
		 *	Defines perspective viewing volume.
		 *
		 *	\param FOVY angle of the field of view in the XZ plane being value
		 *		within 0..180
		 *	\param aspect aspect ratio of the frustum (width / height)
		 *	\param near near clipping plane's distance from viewer (positive)
		 *	\param far far clipping plane's distance from viewer (positive)
		 */
		virtual vVoid SetPerspective(const vReal FOVY, const vReal aspect, const vReal near, const vReal far) = 0;

		/*!
		 *	Defines a pixel rectangle in the window into which the final image is mapped.
		 *	\param left left of rectangle
		 *	\param bottom bottom of rectangle
		 *	\param width width of rectangle
		 *	\param height height of rectangle
		 */
		virtual vVoid SetViewPort(const vInt &left, const vInt &bottom, const vInt &width, const vInt &height) = 0;

		/*!
		 *	Defines near and far clipping planes.
		 *	\param near distance from viewpoint to near clipping plane
		 *	\param far distance from viewpoint to far clipping plane
		 */
		virtual vVoid SetDepthRange(const vReal near, const vReal far) = 0;

		/*!
		 *	Defines orthographic Projection given "Projection box".
		 *	\param leftBottomNear left, bottom, near corner of Projection box
		 *	\param rightTopFar right, top, far corner of Projection box
		 */
		virtual vVoid SetOrtho(const vVector3D &leftBottomNear, const vVector3D &rightTopFar) = 0;

		/*!
		 *	Loads identity transformation.
		 */
		virtual vVoid LoadIdentity() = 0;

		/*!
		 *	Loads translation.
		 *	\param vector translation vector
		 */
		virtual vVoid LoadTranslate(const vVector3D &vector) = 0;

		/*!
		 *	Loads translation discarding Z component (Z = 0).
		 *	\param vector translation vector
		 */
		virtual vVoid LoadTranslate(const vVector2D &vector) = 0;

		/*!
		 *	Loads rotation around specified vector.
		 *	\param vector ray starting in origin around which translation
		 *		is being done
		 *	\param angle angle to Rotate around
		 */
		virtual vVoid LoadRotate(const vReal angle, const vVector3D &vector) = 0;

		/*!
		 *	Loads rotation around Z axis.
		 *	Especially convenient when using orthogonal Projection mode.
		 *	\param angle angle to Rotate around
		 */
		virtual vVoid LoadRotateZ(const vReal angle) = 0;

		/*!
		 *	Loads Scale transformation.
		 *	\param vector Scale vector
		 */
		virtual vVoid LoadScale(const vVector3D &vector) = 0;

		/*!
		 *	Loads given transformation.
		 *	\param matrix matrix to be Loaded as current transformation
		 */
		virtual vVoid LoadMatrix(const vMatrix4D &matrix) = 0;

		/*!
		 *	Performs translation.
		 *	\param vector translation vector
		 */
		virtual vVoid Translate(const vVector3D &vector) = 0;

		/*!
		 *	Performs translation discarding Z axis (Z = 0).
		 *	\param vector translation vector
		 */
		virtual vVoid Translate(const vVector2D &vector) = 0;

		/*!
		 *	Performs translation discarding Z axis (Z = 0).
		 *	\param x x coordinate to Translate by
		 *	\param y y coordinate to Translate by
		 */
		virtual vVoid Translate(const vReal x, const vReal y) = 0;

		/*!
		 *	Performs translation along X axis discarding Y & Z axis.
		 *	\param x translation along X axis
		 */
		virtual vVoid TranslateX(const vReal x) = 0;

		/*!
		 *	Performs translation along Y axis discarding X & Z axis.
		 *	\param x translation along Y axis
		 */
		virtual vVoid TranslateY(const vReal y) = 0;

		/*!
		 *	Performs rotation.
		 *	\param vector rotation vector
		 *	\param angle angle to Rotate around
		 */
		virtual vVoid Rotate(const vReal angle, const vVector3D &vector) = 0;

		/*!
		 *	Performs rotation around Z axis.
		 *	Especially convenient when using orthogonal Projection mode.
		 *
		 *	\param angle angle to Rotate around.
		 */
		virtual vVoid RotateZ(const vReal angle) = 0;

		/*!
		 *	Performs rotation around Y axis.
		 *	
		 *	\param angle angle to Rotate around.
		 */
		virtual vVoid RotateY(const vReal angle) = 0;

		/*!
		 *	Performs rotation around X axis.
		 *
		 *	\param angle angle to Rotate around.
		 */
		virtual vVoid RotateX(const vReal angle) = 0;

		/*!
		 *	Performs scaling.
		 *	\param vector Scale vector
		 */
		virtual vVoid Scale(const vVector3D &vector) = 0;

		/*!
		 *	Multiplies current matrix by given.
		 *	\param matrix matrix to be applied
		 */
		virtual vVoid MultMatrix(const vMatrix4D &matrix) = 0;

		/*!
		 *	Makes "camera" Look in custom way.
		 *	\param eyeVertex viewing point
		 *	\param tarVertex desired point to Look at
		 *	\param upVector vector from viewing point in "up" direction
		 */
		virtual vVoid LookAt(const vVector3D &eyeVertex, const vVector3D &tarVertex, const vVector3D &upVector) = 0;

		/*!
		 *	Pushes current matrix into proper stack.
		 *	Depending on matrix mode, matrix may go to model-view matrices stack
		 *	or Projection matrices stack.
		 */
		virtual vVoid PushMatrix() = 0;

		/*!
		 *	Pops top matrix from the current matrices stack.
		 */
		virtual vVoid PopMatrix() = 0;

		/*!
		 *	Enables orthographic Projection
		 */
		virtual vVoid EnableOrtho() = 0;

		/*!
		 *	Disables orthographic Projection
		 */
		virtual vVoid DisableOrtho() = 0;

		/*!
		 *	Enables scissor test.
		 *	Doing it assures every further operation till next disabling scissors
		 *	will not show outside specified rectangle.
		 *	\param rectangle test rectangle
		 */
		virtual vVoid EnableScissors(const vRectangle& rectangle) = 0;

		/*!
		 *	Disables scissor test.
		 */
		virtual vVoid DisableScissors() = 0;

		/*!
		 *	Pushes scissor box onto stack (and Enables it if not Enabled before).
		 *	\param rectangle test rectangle
		 */
		virtual vVoid PushScissors(const vRectangle& rectangle) = 0;

		/*!
		 *	Pops scissor box from scissor boxes stack.
		 *	When last scissors where Popped, then scissor test s Disabled.
		 *	\param rectangle test rectangle
		 */
		virtual vVoid PopScissors() = 0;
		
		/*!
		 *	Projects given vertex using current Projection matrix.
		 *	\param vertex vertex to Project
		 *	\param screenCoords screen coordinates to be returned as a result
		 */
		virtual vVoid Project(const vVector3D &vertex, vVector2D &screenCoords) = 0;

		/*!
		 *	UnProjects given screen coords using current Projection matrix.
		 *	\param vertex vertex to be returned as a result
		 *	\param screenCoords screen coordinates of a point to Unproject
		 */
		virtual vVoid Unproject(vVector3D &vertex, const vVector2D &screenCoords) = 0;

		/*!
		 *	Returns current translation on XY axis relative to (0,0).
		 *	\attention It does work assuming no rotations and no scaling was applied
		 *		in the meantime (the only allowed transformation is translation)
		 */
		virtual vVector2D Translation() const = 0;

		/*!
		 *	Returns current view point origin.
		 *
		 *	\return current view point origin.
		 */
		virtual vVector3D Origin() const = 0;

		/*!
		 *	Returns current viewing angles.
		 *
		 *	\param current viewing angles
		 */
		virtual vVector3D Angles() const = 0;

		/*!
		 *	Returns current model-view matrix.
		 *
		 *	\param matrix matrix to be returned
		 */
		virtual vVoid ModelViewMatrix(vMatrix4D &matrix) const = 0;

		/*!
		 *	Returns current Projection matrix.
		 *
		 *	\param matrix matrix to be returned
		 */
		virtual vVoid ProjectionMatrix(vMatrix4D &matrix) const = 0;

		/*! \brief Return frustum.
		 *
		 *	Frustum should be called after camera setup,
		 *	it retrieves frustum information directly from API.
		 *	
		 *	\return	vFrustum*	Retrieved frustum.
		 *	\sa		vFrustum.
		 */	
		inline vFrustum* Frustum() {
			mpFrustum->Extract();

			return mpFrustum;
		}

	protected:

		/*----------------- Allowed matrix modes ------------------*/
		
		#define	MODELVIEW_MATRIX_MODE	0	///< Model view matrix mode
		#define	PROJECTION_MATRIX_MODE	1	///< Projection matrix mode

		
		/// Current matrix mode (one of model-view or Projection)
		vInt mMatrixMode;

		/// Stack for model-view matrices.
		vStack<vMatrix4D> mModelViewMatricesStack;

		///	Stack for Projection matrices.
		vStack<vMatrix4D> mProjectionMatricesStack;

		/// Stack for scissor boxes
		vStack<vRectangle> mScissorsStack;

		vFrustum* mpFrustum;
	};
}

#endif //__VVIEW_H__